home *** CD-ROM | disk | FTP | other *** search
/ Nebula 1 / Nebula One.iso / Utilities / Converters / Convert_RTF / Source / rtfConverter.m < prev    next >
Text File  |  1995-06-12  |  73KB  |  2,009 lines

  1. /***********************************************************************\
  2. Converter class for Convert RTF which converts between Mac and NeXT rtf formats.
  3. Copyright (C) 1993 David John Burrowes
  4.  
  5. This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 1, or (at your option) any later version.
  6.  
  7. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License for more details.
  8.  
  9. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
  10.  
  11. The author, David John Burrowes, can be reached at:
  12.     davidjohn@kira.net.netcom.com
  13.     David John Burrowes
  14.     1926 Ivy #10
  15.     San Mateo, CA 94403-1367
  16. \***********************************************************************/
  17. #import "rtfConverter.h"
  18. #import "rtfController.h"            // <--- Ugly hack!
  19. #import "rtfFile.h"
  20. #import "rtfToken.h"
  21. #import <stdio.h>
  22. #import <stdlib.h>
  23. #import <string.h>
  24. #import <objc/HashTable.h>
  25. #import "MacToNeXTRTFText.h"
  26. #import "NeXTToMacRTFText.h"
  27. #import "FontEntry.h"
  28. #import <appkit/publicWraps.h> 
  29. #import <appkit/Listener.h>        // for the drag-and-drop of files facility
  30. #import <appkit/Speaker.h>        // ibid
  31. #import <appkit/Application.h>        // To get NXApp (I thought that is what defaults was...)
  32. #import    "djbflag.h"
  33.  
  34. @implementation rtfConverter
  35.  
  36.  
  37. /*######################################################################*\
  38.  
  39. HISTORY:
  40.     93.04.03    djb    Added code for a bugfix for character conversions.
  41.     Here's the scoop.  Given a document that was of the form:
  42.         {...\f23 blahblah {\f45 foo foo} blah blah...}
  43.     If \f23 was, say, Symbol, and \f45 was Times, then what used to happen is that
  44.     the blahblah would not have characters converted, then, the \f45 would change
  45.     the conversion flag, and 'foo foo' would be, and then 'blah blah' would also be converted
  46.     because I was ignoring group boundraries.  oops.  This will now do that conversion
  47.     properly if the user has chosen to have only standard fonts converted.  This involves
  48.     maintaining a stack of conversion values that we push and pop for each group, so
  49.     we can revert to the previous conversion values easily.  Note that examineFile still
  50.     does not do essentially any processing of whether individual text should be converted,
  51.     and so it wasn't affected by this bug, or the fix.
  52.  
  53. \*######################################################################*/
  54.  
  55.  
  56. //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  57. //    Method:        init
  58. //    Parameters:    none
  59. //    Returns:     none
  60. //    Stores:        none
  61. //    Description:
  62. //        Initalizes an instance... namely by defining th manager instance as null
  63. //    Bugs:
  64. //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  65. - init
  66. {
  67.     [super   init];
  68.     MacTextConverter = [[MacToNeXTRTFText   alloc]  init];
  69.     NeXTTextConverter = [[NeXTToMacRTFText   alloc] init];
  70.     colorsUsed = NO;
  71.     SymbolNumber = -1;
  72.     addedSymbol = NO;
  73.     numPicts = 0;    // Number of pictures found in the file
  74.     pictNum = 0;        // Number of the last picture found.
  75.     //
  76.     //    These will presumably be set by the caller, we just define them just in case
  77.     //
  78.     RemoveUnderline = NO;
  79.     AlterSingleQuotes = NO;
  80.     ConvertAllText = ConvertAll;    // Modified for change from boolean to 3 optins
  81.     ExtractPicts = NO;
  82.     ConvertPictures =YES;
  83.     //
  84.     //    KeepTokensForMe is intended only for the original author.  No one else
  85.     //    probably could care less.  It stores the various \pich, etc tokens from
  86.     //    a pict group in another file.  These are usually useless details though.
  87.     //
  88.     if (ForDavidJohnBurrowesOnly == 1)
  89.         KeepTokensForMe = YES;
  90.     else
  91.         KeepTokensForMe = NO;
  92.     PictConverterIsOpen = NO;
  93.     //
  94.     //    93.04.03    djb    Added as part of char convert bugfix
  95.     //
  96.     CharConvertStack = (ConvertTypes*) malloc(50*sizeof(ConvertTypes));
  97.     StackTop = 49;
  98.     StackLocation = 0;
  99.     return self;
  100. }
  101.  
  102.  
  103. //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  104. //    Method:        free
  105. //    Parameters:    none
  106. //    Returns:     none
  107. //    Stores:        none
  108. //    Description:
  109. //        Frees the object.  This involves killing the pict converter if we opened it.
  110. //    Bugs:
  111. //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  112. - free
  113. {
  114.     int            result, flag;
  115.     Instance        ourSpeaker;
  116.     port_t        thePort;
  117.  
  118.     if (PictConverterIsOpen == YES)
  119.     {
  120.         thePort = NXPortFromName("Convert_PICT", NULL);
  121.         if (thePort != PORT_NULL)
  122.         {
  123.             ourSpeaker = [NXApp appSpeaker];
  124.             [ourSpeaker   setSendPort: thePort ];
  125.             result = [ourSpeaker   msgQuit: &flag];
  126.         }
  127.     }
  128.     [super free];
  129.     return self;
  130. }
  131.  
  132. //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  133. //    Method:        isThisAGoodFile:
  134. //    Parameters:    A File instance
  135. //    Returns:     YES if it is, NO if it isn't.
  136. //    Stores:        none
  137. //    Description:
  138. //        This method attempts to determine if the given file (being accessed as an rtfFile
  139. //        instance) is indeed an RTF one.  A file is judged to be an RTF one iff it's first
  140. //        two tokens are {\rtf#.  Note that it restores the file's position when done.
  141. //    Bugs:
  142. //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  143. - (Boolean) isThisAGoodFile: fileInstance
  144. {
  145.     Instance        thetoken;
  146.     Boolean         isRTF             = NO;
  147.     Integer        storedLocation    = [fileInstance   GetCurrentPosition];
  148.     CString        tempName;
  149.  
  150.     [fileInstance   MoveTo: 0];   
  151.     thetoken = [fileInstance   GetToken];
  152.     if ([thetoken   GetType] == tokenBeginGroup)
  153.         isRTF = YES;
  154.     [thetoken   free];
  155.     
  156.     thetoken = [fileInstance   GetToken];
  157.     tempName = [thetoken  GetName];
  158.     if ((strcmp(tempName, "rtf") == 0) && (isRTF == YES))
  159.         isRTF = YES;
  160.     else
  161.     {
  162.         [self  PutCString: "This does not appear to be an rtf file (because the first two tokens were not `{\\rtf').  Proceed with caution!" Into: SECOND_RESULT];
  163.         isRTF = NO;
  164.     }
  165.     FreeCString(tempName);
  166.     [thetoken   free];
  167.     [fileInstance   MoveTo: storedLocation];   
  168.  
  169.     return isRTF;
  170. }
  171.  
  172.  
  173. //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  174. //    Method:        SetConversionDirection:
  175. //    Parameters:    A boolean value. YES if to assume files are Mac RTF files.  No if not.
  176. //    Returns:     self
  177. //    Stores:        none
  178. //    Description:
  179. //        If the user has asked that files, by default, be assumed to be Mac rtf files,
  180. //        this will be alled with YES.  If the user prefers to assume RTF files are
  181. //        going to be processed, NO is passed.
  182. //    Bugs:
  183. //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  184. - SetConversionDirection: (Boolean) convertAsMacRTF
  185. {
  186.     if (convertAsMacRTF == YES)
  187.         ConvertSource = MacRTF;
  188.     else
  189.         ConvertSource = NeXTRTF;
  190.  
  191.     return self;
  192. }
  193.  
  194.  
  195. //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  196. //    Method:        RemoveFirstNeXTUnderline:
  197. //    Parameters:    A boolean value. YES if we should remove the first \ul0 in an rtf file.
  198. //    Returns:     self
  199. //    Stores:        none
  200. //    Description:
  201. //        If the source file is a NeXT rtf file (ConvertSource is NeXTRTF), then if our
  202. //        parameter is YES, then we will remove the first \ul0 in the file 
  203. //    Bugs:
  204. //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  205. - RemoveFirstNeXTUnderline: (Boolean) StripFirstUL0
  206. {
  207.     RemoveUnderline = StripFirstUL0;
  208.     return self;
  209. }
  210.  
  211.  
  212. //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  213. //    Method:        ConvertTheSingleQuotes:
  214. //    Parameters:    A boolean value. YES if we should always change the ascii single quotes
  215. //                to equivalent looking quotes on the destination platform.  Otherwise, let
  216. //                them appear on thedestination as thye would there. (e.g. next single quotes
  217. //                look curley.  mac ones are straight and an accent.  if this is yes, the next
  218. //                quotes will be mapped to curley quotes on the mac, not the default ones)
  219. //    Returns:     self
  220. //    Stores:        none
  221. //    Description:
  222. //        Stores the specified parameter until later processing.
  223. //    Bugs:
  224. //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  225. - ConvertTheSingleQuotes: (Boolean) changeQuotes
  226. {
  227.     AlterSingleQuotes = changeQuotes;
  228.     return self;
  229. }
  230.  
  231.  
  232. //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  233. //    Method:        SetTextConversion:
  234. //    Parameters:    A boolean value. YES if we should convert the special characters for
  235. //                all fonts, NO if we should only do it for 'standard' fonts.
  236. //    Returns:     self
  237. //    Stores:        none
  238. //    Description:
  239. //        Stores the specified parameter until later processing.
  240. //    Bugs:
  241. //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  242. - SetTextConversion: (GuiConvertChoices) convertText
  243. {
  244.     ConvertAllText = convertText;
  245.     return self;
  246. }
  247.  
  248.  
  249. //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  250. //    Method:        SetPictConversion:AndDataRemoval:
  251. //    Parameters:    YES if we are to convert Mac PICT data
  252. //                YES if we are to remove all traces of pict data
  253. //    Returns:     self
  254. //    Stores:        none
  255. //    Description:
  256. //        This sets two conversion options in this class.  The first specifies whether
  257. //        we should try to use the Convert_PICT program to convert any PICT data
  258. //        we encounter.  The second is a bit more complex.  This converter always removes
  259. //        the {\pict\macpict...} data from the rtf file.  The question is: do we discard it
  260. //        completely (after perhaps doing the above conversion), or do we store it in an
  261. //        another file (a .pict file).  This latter is only of interest if the user doesn't want
  262. //        to loose the original pict data for one reason or another.  I'm such a person, and
  263. //        so I'd prefer to keep it around.
  264. //    Bugs:
  265. //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  266. - SetPictConversion: (Boolean) ConvertPictData AndDataRemoval: (Boolean) DeletePictData;
  267. {
  268.     if (DeletePictData == YES)
  269.         ExtractPicts = NO;
  270.     else
  271.         ExtractPicts = YES;
  272.     
  273.     ConvertPictures = ConvertPictData;
  274.     return self;
  275. }
  276.  
  277.  
  278. //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  279. //    Routine:        examineFile: 
  280. //    Parameters:    The file to be examined
  281. //    Returns:        self
  282. //    Stores:        error code
  283. //    Description:
  284. //        This walks through the rtf file from start to end, and watches for certain
  285. //        things:
  286. //            1) Whether any color tokens are used (\cf and \cb)
  287. //            2) it counts how many times each type face is used (using \f)
  288. //                (allowing us to judge what to write out in the font table)
  289. //            3) It stores whether Symbol may be needed for some character conversions (in \')
  290. //            4) Counts occurrences of PICT images (\pict)
  291. //        As part of the work for counting font use, we read in the whole font table, and
  292. //        store it as a fontTable variable.  We then later increment usage counts in the
  293. //        individual FontEntry instances.  (this table is later used to write out the font table)
  294. //    Bugs
  295. //        When computing whether symbol was used while processing \', we ignore
  296. //        the 'current' font at that point.  This may have the effect of leaving the count
  297. //        at least partially inaccurate, since those characters may not actually be converted
  298. //        later on.
  299. //    History:
  300. //        93.01.24    Added storing of the lasterrorcode for the while, because in circumstances
  301. //                where the end of file occurrs at a point where we want to update the
  302. //                progress indicator, we'd wipe out the sourceFile's error code, and produce
  303. //                a crash.
  304. //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  305. - examineFile: sourceFile
  306. {
  307.     Instance        thetoken, temptoken;
  308.     TokenType    theType;
  309.     CString        theName;
  310.     Real            modifier        = 30.0 / [sourceFile   FileSize];
  311.     Integer        groupDepth    = 0;
  312.     Character    aCharacter;
  313.     Instance        myEntry;
  314.     Integer        tokenCount    = 0;
  315.     CString        tempName;
  316.     Integer        lastErrorCode;
  317.     Character    fontName[31];    // To hold UnnamedFont32767  (for instance)
  318.     Integer        fontNumber;
  319.  
  320.     [sourceFile   MoveTo: 0];
  321.     thetoken = [sourceFile   GetNextControlToken];
  322.     lastErrorCode = [sourceFile  GetErrorCode];
  323.     theName = [thetoken   GetName];
  324.  
  325.     while ( lastErrorCode == ERR_OK )
  326.     {
  327.         //
  328.         //    If we just found the \fonttbl token.  Consume the font table and turn it into our
  329.         //    hashed fontTable structure.
  330.         //
  331.         if (strcmp(theName, "fonttbl") == 0)
  332.         {
  333.             groupDepth = 1;
  334.             while (groupDepth > 0)
  335.             {
  336.                 //
  337.                 //    Watch for the end of the table (when we have
  338.                 //    found a matching number of {'s and }'s  (measured by groupDepth).
  339.                 //    Store the font information
  340.                 //
  341.                 [thetoken   free];
  342.                 FreeCString(theName);
  343.                 thetoken = [sourceFile   GetToken];
  344.                 theType = [thetoken   GetType];
  345.                 theName = [thetoken   GetName];
  346.                 if (theType ==  tokenBeginGroup)
  347.                     groupDepth ++;
  348.                 else if (theType == tokenEndGroup)
  349.                     groupDepth --;
  350.                 else if (theType == tokenControlWord)
  351.                 {
  352.                     if (strcmp(theName, "f") == 0)
  353.                     {
  354.                         myEntry = [[FontEntry alloc] init];
  355.                         [myEntry  SetNumber: [thetoken GetValue]];
  356.                         [thetoken   free];
  357.                         //
  358.                         //    Assume the next token is our type token, and that is followed
  359.                         //    by the font name token.
  360.                         //
  361.                         [myEntry  SetFontType: [sourceFile   GetToken]];
  362.                         thetoken = [sourceFile   GetFamilyNameToken];
  363.                         tempName = [thetoken   GetName];
  364.                         [myEntry   SetName: tempName];
  365.                         [myEntry   PrepName];
  366.                         FreeCString(tempName);
  367.                         //
  368.                         //    Add the entry to the font table
  369.                         //
  370.                         [fontTable   insertKey:  (const void *) [myEntry   GetNumber]
  371.                             value: (void *) myEntry];
  372.                         //
  373.                         //    If this entry described symbol, get the number for reference
  374.                         //
  375.                         tempName = [myEntry   GetName];
  376.                         if (strcmp(tempName, "Symbol") == 0)
  377.                             SymbolNumber = [myEntry  GetNumber];
  378.                         FreeCString(tempName);
  379.                     }
  380.                 }    /*    End of if-then-else on a token type */
  381.             }
  382.         }
  383.         else if (strcmp(theName, "f") == 0)
  384.         {
  385.             //
  386.             //    Count how often each font is used. (assumes all \fxx are in the font table) 
  387.             //
  388.             fontNumber =  [thetoken   GetValue];
  389.             myEntry = [fontTable valueForKey:  (const void *) fontNumber];
  390.             if (myEntry== nil)
  391.             {
  392.                 //
  393.                 //    93.02.10    djb    Added when I realized I really did want a clear record
  394.                 //    of all fonts being used in the document, even if I didn't know their
  395.                 //    names (this is useful, because you might know what \f201 refers to,
  396.                 //    and thus be able to manually fix the font table.  This chunk of code has
  397.                 //    the effect of creating a new font entry in the table which will then
  398.                 //    have it's count incremented and be written out in the final thing
  399.                 //
  400.                 myEntry = [[FontEntry alloc] init];
  401.                 [myEntry   SetNumber:  fontNumber];
  402.                 sprintf(fontName, "UnnamedFont%d", (int) fontNumber);
  403.                 [myEntry   SetName: fontName];
  404.                 temptoken=[[rtfToken alloc] initTokenOfType: tokenControlWord];
  405.                 [temptoken   SetTokenName:   "fnil"];
  406.                 [myEntry   SetFontType: temptoken];
  407.                 [fontTable   insertKey:  (const void *) fontNumber
  408.                     value: (void *) myEntry];
  409.             }
  410.             [myEntry   IncrementCount];
  411.         }
  412.         //
  413.         //    Check whether color values are used.
  414.         //
  415.         else  if ( (strcmp(theName, "cf") == 0) || (strcmp(theName, "cb") == 0) )
  416.                 colorsUsed = YES;
  417.         else if  (ConvertSource == MacRTF)
  418.         {
  419.             //
  420.             //    Do some Mac specific conversions.
  421.             //
  422.             if (strcmp(theName, "\'") == 0)
  423.             {
  424.                 //
  425.                 //    We found a \' token.  Check whether this will be converted into
  426.                 //    Symbol (BUG: We ignore the actual font in effect at this piont)
  427.                 //    If it will be, the increment the Symbol count.  (we determine if the
  428.                 //    char will be converted to symbol by actually converting it and
  429.                 //    checking the result code.  ICK!
  430.                 //
  431.                 aCharacter = (Character)  [thetoken   GetValue];
  432.                 [CharConverter   ConvertCharacter: aCharacter];
  433.                 if  ([CharConverter   GetBooleanFrom: SECOND_RESULT] == YES)
  434.                 {
  435.                     //
  436.                     //    If symbol wasn't in the font entry table, pick it a number,
  437.                     //    by claiming the first unused number
  438.                     //
  439.                     if (SymbolNumber == -1)
  440.                     {
  441.                         SymbolNumber = 1;
  442.                         while ([fontTable   isKey: (const void *) SymbolNumber] == YES)
  443.                             SymbolNumber++;
  444.                         myEntry = [[FontEntry alloc] init];
  445.                         [myEntry   SetNumber: SymbolNumber];
  446.                         [myEntry   SetName: "Symbol"];
  447.                         temptoken=[[rtfToken alloc] initTokenOfType: tokenControlWord];
  448.                         [temptoken   SetTokenName:   "ftech"];
  449.                         [myEntry   SetFontType: temptoken];
  450.                         [fontTable   insertKey:  (const void *) SymbolNumber
  451.                             value: (void *) myEntry];
  452.                         addedSymbol = YES;
  453.                     }
  454.                     //
  455.                     //    Now that we're garantueed that symbol is in here,
  456.                     //    increment it's count
  457.                     //
  458.                     myEntry = [fontTable valueForKey:  (const void *) SymbolNumber] ;
  459.                     [ myEntry  IncrementCount ];
  460.                 }
  461.             }
  462.             else if (strcmp(theName, "pict") == 0)
  463.             {
  464.                 //
  465.                 //    If we find a \pict instruction, check to see if it is a Mac picture,
  466.                 //    and increment a count if so.
  467.                 //
  468.                 [thetoken   free];
  469.                 FreeCString(theName);
  470.                 thetoken = [sourceFile   GetToken];
  471.                 theName = [thetoken  GetName];
  472.                 if (strcmp(theName, "macpict") == 0)
  473.                     numPicts++;
  474.                 //
  475.                 //    Force the us to ping the mamager, since sometimes
  476.                 //    pictures are big enough to cause the update on the screen not to work
  477.                 //    right.
  478.                 [myManager   SetPercentageDone: modifier *
  479.                                 [sourceFile  GetCurrentPosition]];
  480.             }
  481.         }
  482.         //
  483.         //    BUG: We should be walking through each word token, and if one would convert
  484.         //    to symbol, then setting symbol as above.
  485.         //
  486.         [thetoken   free];
  487.         FreeCString(theName);
  488.         thetoken = [sourceFile   GetNextControlToken];
  489.         lastErrorCode = [sourceFile   GetErrorCode];
  490.         if (thetoken != NullInstance)
  491.             theName = [thetoken   GetName];
  492.         else
  493.             theName = NullCString;
  494.  
  495.         if ((tokenCount++ % 100) == 0)
  496.             [myManager   SetPercentageDone: modifier * [sourceFile  GetCurrentPosition]];
  497.      }
  498.  
  499.     [thetoken   free];
  500.     FreeCString(theName);
  501.  
  502.     [self   StoreErrorCode:  ERR_OK AndText: "OK"];
  503.     return self;
  504. }
  505.  
  506.  
  507.  
  508. //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  509. //    Routine:        PrepareFontTable
  510. //    Parameters:    none
  511. //    Returns:        self
  512. //    Stores:        none directly
  513. //    Description:
  514. //        Given the instance's fontTable, we walk through each entry, and convert the
  515. //        font names based on which way we are converting.  e.g. the Mac's
  516. //        Avant Garde is called AvantGarde-Book on the NeXT.  Thus, for all the standard
  517. //        Mac fonts, we make sure they will come up right on the NeXT.  And vice versa.
  518. //        Some fonts should not have their characters converted at all (e.g. Zaph Dingbats)
  519. //        because they have the same layout on both platforms.  Otherwise, we use
  520. //        whatever conversion option the user has chosen.
  521. //    Bugs
  522. //        A NeXT font can be specified as, say, AvantGarde-Italic.  Because NeXt utilities
  523. //        don't always record just the family name, it becomes very difficult to retain
  524. //        this information.  So.  We do not.
  525. //    History:
  526. //        93.02.15    djb    Added hack to the end: If the user has chosen to convert NO fonts,
  527. //                    then override    whatever we've already put in and set it to none.
  528. //                    This is a bit of a kludge, but it suffices for now.
  529. //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  530. - PrepareFontTable
  531. {
  532.     NXHashState    state            = [fontTable initState]; 
  533.     Integer            fontNumber;
  534.     Instance            fontEntry;
  535.     CString            fontName;
  536.     //
  537.     //    For each fontTable entry, massage the name and the conversion options
  538.     //
  539.     while  ([fontTable   nextState: &state
  540.                 key: (void **)&fontNumber
  541.                 value: (const void **)&fontEntry] == YES) 
  542.     {
  543.         //
  544.         //    Set the default type of conversion.
  545.         //
  546.         [fontEntry   SetConversion: FullConversion];
  547.         fontName = [fontEntry   GetName];
  548.  
  549.         if (ConvertSource == MacRTF)
  550.         {
  551.             //
  552.             //    Convert type family names
  553.             //
  554.             if (strcmp(fontName, "Times") == 0)
  555.                 [fontEntry   SetName: "Times-Roman"];
  556.             else if (strcmp(fontName, "Avant Garde") == 0)
  557.                 [fontEntry   SetName: "AvantGarde-Book"];
  558.             else if (strcmp(fontName, "Bookman") == 0)
  559.                 [fontEntry   SetName: "Bookman-Light"];
  560.             else if (strcmp(fontName, "Helvetica Narrow") == 0)
  561.                 [fontEntry   SetName: "Helvetica-Narrow"];
  562.             else if (strcmp(fontName, "New Century Schlbk") == 0)
  563.                 [fontEntry   SetName: "NewCenturySchlbk-Roman"];
  564.             else if (strcmp(fontName, "Palatino") == 0)
  565.                 [fontEntry   SetName: "Palatino-Roman"];
  566.             else if (strcmp(fontName, "Zapf Chancery") == 0)
  567.                 [fontEntry   SetName: "ZapfChancery-MediumItalic"];
  568.             else if (strcmp(fontName, "New York") == 0)
  569.                 [fontEntry   SetName: "NewYork"];
  570.             else if (strcmp(fontName, "Los Angeles") == 0)
  571.                 [fontEntry   SetName: "LosAngeles"];
  572.             else if (strcmp(fontName, "San Francisco") == 0)
  573.                 [fontEntry   SetName: "SanFrancisco"];
  574.             //
  575.             //    Also, mark certain type families as requiring no character conversions
  576.             //
  577.             else     if (strcmp(fontName, "Symbol") == 0)
  578.                 [fontEntry   SetConversion: NoConversion];  // Identical on both platforms
  579.             else if (strcmp(fontName, "Zapf Dingbats") == 0)
  580.             {
  581.                 [fontEntry   SetName: "ZapfDingbats"];
  582.                 [fontEntry   SetConversion: NoConversion]; // Identical on both platforms.
  583.             }
  584.             else if (strcmp(fontName, "Cairo") == 0)
  585.                 [fontEntry   SetConversion: NoConversion];
  586.             else if (strcmp(fontName, "Mobile") == 0)
  587.                 [fontEntry   SetConversion: NoConversion];
  588.             else if (strcmp(fontName, "Taliesin") == 0) 
  589.                 [fontEntry   SetConversion: NoConversion];
  590.             //
  591.             //    The following families require no name changes, and take the FullConversion
  592.             //    If a name isn't in the above or below, we set it's conversion options to
  593.             //    whatever the user specified
  594.             //
  595.             else if ( (strcmp(fontName, "Geneva") != 0)  &&
  596.                 (strcmp(fontName, "Courier") != 0)  && 
  597.                 (strcmp(fontName, "Helvetica") != 0)  && 
  598.                 (strcmp(fontName, "Monaco") != 0)  && 
  599.                 (strcmp(fontName, "Athens") != 0)  && 
  600.                 (strcmp(fontName, "Toronto") != 0)  && 
  601.                 (strcmp(fontName, "Venice") != 0)  && 
  602.                 (strcmp(fontName, "London") != 0)  && 
  603.                 (strcmp(fontName, "Chicago") != 0)  && 
  604.                 (strcmp(fontName, "Seattle") != 0) )
  605.             {
  606.                 if (ConvertAllText == ConvertAll)
  607.                     [fontEntry   SetConversion: FullConversion];
  608.                 else
  609.                     [fontEntry   SetConversion: NoConversion];
  610.             }
  611.         }
  612.         else    // NeXTRTF
  613.         {
  614.             //
  615.             //    Convert names of type faces
  616.             //
  617.             if (strcmp(fontName, "Times-Roman") == 0)
  618.                 [fontEntry   SetName: "Times"];
  619.             else if (strncmp(fontName, "AvantGarde", 10) == 0)
  620.                 [fontEntry   SetName: "Avant Garde"];
  621.             else if (strncmp(fontName, "Bookman",7) == 0)
  622.                 [fontEntry   SetName: "Bookman"];
  623.             else if (strcmp(fontName, "Helvetica-Narrow") == 0)
  624.                 [fontEntry   SetName: "Helvetica Narrow"];
  625.             else if (strncmp(fontName, "NewCenturySchlbk", 16) == 0)
  626.                 [fontEntry   SetName: "New Century Schlbk"];
  627.             else if (strncmp(fontName, "Palatino", 8) == 0)
  628.                 [fontEntry   SetName: "Palatino"];
  629.             else if (strncmp(fontName, "ZapfChancery", 12) == 0)
  630.                 [fontEntry   SetName: "Zapf Chancery"];
  631.             //
  632.             //    Declare these as being ones that require no conversion
  633.             //
  634.             else     if (strcmp(fontName, "Symbol") == 0)
  635.                 [fontEntry   SetConversion: NoConversion];  // Identical on both platforms
  636.             else if (strncmp(fontName, "ZapfDingbats",12) == 0)
  637.             {
  638.                 [fontEntry   SetName: "Zapf Dingbats"];
  639.                 [fontEntry   SetConversion: NoConversion]; // Identical on both platforms.
  640.             }
  641.             //
  642.             //    The following families require no name changes, and take the FullConversion
  643.             //    If a name isn't in the above or below, we set it's conversion options to 
  644.             //    whatever the user specified
  645.             //
  646.             else if ( (strcmp(fontName, "Courier") != 0)  && 
  647.                 (strcmp(fontName, "Helvetica") != 0)  && 
  648.                 (strcmp(fontName, "Ohlfs") != 0)  )
  649.             {
  650.                 if (ConvertAllText == ConvertAll)
  651.                     [fontEntry   SetConversion: FullConversion];
  652.                 else
  653.                     [fontEntry   SetConversion: NoConversion];
  654.             }
  655.         }
  656.         //
  657.         //    93.02.15    djb    Added this to force no conversion if the user has chosen.
  658.         //                Note that by now we've polarized the 3 options of ConvertAllText
  659.         //                to the two that a FontEntry understands.
  660.         //    
  661.         if (ConvertAllText == ConvertNone)
  662.             [fontEntry   SetConversion: NoConversion];
  663.  
  664.         FreeCString(fontName);
  665.     }
  666.  
  667.  
  668.     return self;
  669. }
  670.  
  671.  
  672. //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  673. //    Routine:        WriteFontTableEntriesTo:
  674. //    Parameters:    the file object to write to
  675. //    Returns:        self
  676. //    Stores:        none directly
  677. //    Description:
  678. //        This takes the instance fontTable object, and iterates through it, writing each
  679. //        font entry out to the destination file.  Each entry will have the form
  680. //            {\fxx \yyy fontname ; }
  681. //        Naturally, this assumes that the fontTable was created and filled elsewhere.
  682. //        This does *not* destroy the fontTable's entries.
  683. //    History
  684. //        93.07.07    djb    Bugfix for 1.1 to convert the characters in the name of the font.
  685. //    Bugs
  686. //        By virtue of the way these are read in, this writes out fonts in the reverse order.
  687. //        Thus \f23 is written before \f1.  This is probably not a big deal, just unaesthetically
  688. //        pleasing.
  689. //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  690. - WriteFontTableEntriesTo: destinationFile
  691. {
  692.     NXHashState    state        = [fontTable initState]; 
  693.     Integer            fontNumber;
  694.     Instance            fontEntry;
  695.     Instance            temptoken;
  696.     CString            tempName;
  697.     //
  698.     //    Step to the next entry in the fontTable.  If there is a next entry, we get
  699.     //    YES and continue
  700.     //
  701.     while  ([fontTable   nextState: &state
  702.                 key: (void **)&fontNumber
  703.                 value: (const void **)&fontEntry] == YES) 
  704.     {
  705.         //
  706.         //    If this font was used more than 0 times, we will write it out.
  707.         //    (Thus, we avoid writing out the unused entries that Word so happily writes)
  708.         //
  709.         if ([fontEntry   GetCount] > 0)
  710.         {
  711.             //
  712.             //    Write the {\fxx
  713.             //
  714.             temptoken=[[rtfToken alloc] initTokenOfType: tokenBeginGroup];
  715.             [destinationFile   WriteToken: temptoken];
  716.             
  717.             temptoken=[[rtfToken alloc] initTokenOfType: tokenControlWord];
  718.             [temptoken   SetTokenName:   "f"];
  719.             [temptoken   SetTokenValue:   (Integer) [fontEntry   GetNumber]];
  720.             [destinationFile   WriteToken: temptoken];
  721.             //
  722.             //    Write the \yyy (the type of the font (e.g. modern)
  723.             //
  724.             [destinationFile   WriteToken: [fontEntry   GetFontType]];
  725.             //
  726.             //    Write:  font-name ; }
  727.             //
  728.             temptoken = [[rtfToken alloc] initTokenOfType: tokenWord];
  729.             tempName = [fontEntry   GetName];
  730.             [temptoken   SetTokenName:   tempName];
  731.             FreeCString(tempName);
  732.             //
  733.             //    93.07.07    djb    A bug reported (by moi) revealed that if there was an 8 bit char
  734.             //                in a font name, it was not being converted when the rtf file was converted.
  735.             //                So, I've changed the straight 'write out' to the method to convert and
  736.             //                then write out.  Note that we always do a full conversion.  Note also
  737.             //                that this is affected by the current quote conversion preference
  738.             //
  739.             [self   ConvertWordIn: temptoken To: destinationFile WithConversion: FullConversion];
  740.  
  741.             temptoken = [[rtfToken alloc] initTokenOfType: tokenWord];
  742.             [temptoken   SetTokenName:   ";"];
  743.             [destinationFile   WriteToken: temptoken];
  744.  
  745.             temptoken=[[rtfToken alloc] initTokenOfType: tokenEndGroup];
  746.             [destinationFile   WriteToken: temptoken];
  747.         }
  748.     }
  749.  
  750.     return self;
  751. }
  752.  
  753.  
  754. //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  755. //    Routine:        ConvertFrom:To: 
  756. //    Parameters:    The file to be converted from, and the file to be converted to
  757. //    Returns:        self
  758. //    Stores:        error code
  759. //    Description:
  760. //        this long routine is the main converter of rtf tokens.
  761. //        After doing lots of initalizations, walk through the file to get a sense of what is there,
  762. //        and to gather all the information about the font table.
  763. //        If necessary, change to an .rtfd file, and start up a pict converter.
  764. //        After this, walk through the source file one token at a time.  In certain cases,
  765. //        such as in the font and color table, we switch into special modes to consume their
  766. //        data properly.  Each time, we keep a count of how deep we are (counting braces)
  767. //        to know when to quit (the inital brace count comes futher down in the file from
  768. //        where we do this consumption, (where we find the initial token in the group).
  769. //        Otherwise, write out the token in a straight forward manner... more or less.
  770. //        Do some character processing on \' tokens.  And, for word tokens, call another
  771. //        method (this one is already plenty big, thank you very much) to do any
  772. //        conversions to be done there.
  773. //    Bugs
  774. //        If the source has foo\'xxbar and this would become fooæbar, this may cause the
  775. //        single 'word' to be split across lines.
  776. //    Reminder:
  777. //        WriteToken takes posession of the token instance.  We should not free it.
  778. //    History:
  779. //        92.12.13    djb    Beginning to clean up from 'proof of concept' form.
  780. //        93.04.03    djb    Modified so used the char conversion stack rather than a local
  781. //                    variable called ConvertChars
  782. //        93.07.07    djb    Enhancement for 1.1.  Added code to convert \bullet to •, \emdash to
  783. //                    —, etc.  when converting a Mac RTF file.
  784. //        93.07.10    djb    Added a bit to the code added on 07.07 so it calls a routine to write the token out
  785. //                    as it is if we haven't found it in any of the special cases.  This addition is a bit of a hack,
  786. //                    since the code is duplicated a line later.  But, this way the multiple if's are avoided if we are
  787. //                    doing a conversion from NeXT.
  788. //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  789. - ConvertFrom: sourceFile To: destinationFile
  790. {
  791.     Real                modifier            =  70.0 / [sourceFile   FileSize];
  792.     CString            theName;
  793.     CString            tempString;
  794.     Integer            GroupDepth        = 0;
  795.     Integer            colorStartDepth    = 0;
  796.     Integer            fontStartDepth    = 0;
  797.     WritingState        state            = normal;
  798.     Boolean            LookingForUL    = NO;
  799.     Integer            tokenCount        = 0;
  800.     Instance            myEntry;
  801.     Instance            thetoken;
  802.     Instance            tempToken;
  803.     TokenType        theType;
  804.     NXHashState    tableState;
  805.     Integer            fontNumber;
  806.     Instance            temptoken;
  807.     [self   ResetResults];
  808.     //
  809.     //    Initalize the instance variables for this file conversion, and set up the
  810.     //    proper character converter.
  811.     //
  812.     pictNum = 0;
  813.     colorsUsed = NO;
  814.     SymbolNumber = -1;
  815.     addedSymbol = NO;
  816.     numPicts = 0;
  817.     [self   ResetStack];            //    93.04.03    djb Added for char conversion bugfix
  818.     if (ConvertSource == MacRTF)
  819.          CharConverter = MacTextConverter;
  820.     else
  821.     {
  822.         CharConverter = NeXTTextConverter;
  823.         //
  824.         //    Set flag to look for underline if user so wishes.
  825.         //
  826.         if ( RemoveUnderline == YES)
  827.             LookingForUL = YES;
  828.     }
  829.     //
  830.     //    Set up the object to hold type family information.
  831.     //
  832.     fontTable = [[HashTable alloc] initKeyDesc:"i" valueDesc: "@"];
  833.     //
  834.     //    Do a first pass through the file, counting various elements for our use here.
  835.     //    Then, convert the new contents of the font table.
  836.     //
  837.     [self   examineFile: sourceFile];
  838.     [self   PrepareFontTable];
  839.     //
  840.     //    If we found PICTs in the file, we must change ourselves to an rtfd file.  Nasty.
  841.     //    (we only do this, though, if we're going to be writing something about them out)
  842.     //
  843.     if  ( (numPicts > 0) &&( (ExtractPicts == YES) || (ConvertPictures == YES)))
  844.         destinationFile = [myManager  MakeDestAnRTFD];
  845.     //
  846.     //    If there's a destination file to convert to, then begin!!!
  847.     //        (the rtfd conversion above might have failed and left us nothing)
  848.     //
  849.     if (destinationFile != NullInstance)
  850.     {
  851.         [sourceFile   MoveTo: 0];
  852.         //
  853.         //    ASSUMPTION:
  854.         //        the first three tokens in the file will be { \rtf and \mac   (or \ansi)
  855.         //        }So, we read these three tokens in, and process accordingly. 
  856.         //        If we fail to find the \rtf,  then we drop into our main converter below.
  857.         //
  858.         thetoken = [sourceFile   GetToken];
  859.         [destinationFile   WriteToken: thetoken];
  860.         GroupDepth ++;
  861.         thetoken = [sourceFile   GetToken];
  862.         theName = [thetoken   GetName];
  863.         if (strcmp(theName, "rtf") == 0)
  864.         {
  865.             [destinationFile   WriteToken: thetoken];
  866.             thetoken = [sourceFile   GetToken];
  867.             if (ConvertSource == MacRTF)    // Ignore whatever it came in with...
  868.                 [thetoken   SetTokenName: "ansi"];
  869.             else
  870.                 [thetoken   SetTokenName: "mac"];
  871.             [destinationFile   WriteToken: thetoken];
  872.             thetoken = [sourceFile   GetToken];
  873.         }
  874.         FreeCString(theName);
  875.         //
  876.         //    Commence main conversion.
  877.         //
  878.         while ( [sourceFile   GetErrorCode] == ERR_OK )
  879.         {
  880.             //
  881.             //    If we're processing the font table:
  882.             //        1) watch begin and end tokens so we know when we're out
  883.             //        2) When we fall out, dump the fonts processed earlier, and change
  884.             //            state back to the normal processing state.
  885.             //
  886.             if (state == inFontTable)
  887.             {
  888.                 theType =[thetoken   GetType];
  889.                 if (theType == tokenBeginGroup)
  890.                 {
  891.                     GroupDepth ++;
  892.                     [thetoken   free];
  893.                 }
  894.                 else if (theType == tokenEndGroup)
  895.                 {
  896.                     GroupDepth --;
  897.                     if (GroupDepth != fontStartDepth)
  898.                         [thetoken   free];
  899.                     else
  900.                     {
  901.                         [self   WriteFontTableEntriesTo: destinationFile];
  902.                         [destinationFile   WriteToken: thetoken]; // need close brace
  903.                         state = normal;
  904.                     }
  905.                 }
  906.                 else
  907.                     [thetoken   free];
  908.             }
  909.             //
  910.             //    If we're processing the color table, and colors were not used in the document
  911.             //    then discard the whole table.  Reset our state when we're done.
  912.             //
  913.             else if ( (state == inColorTable) && (colorsUsed == NO))
  914.             {
  915.                 theType =[thetoken   GetType];
  916.                 if (theType ==  tokenBeginGroup)
  917.                     GroupDepth ++;
  918.                 else if (theType == tokenEndGroup)
  919.                 {
  920.                     GroupDepth --;
  921.                     if (GroupDepth == colorStartDepth)
  922.                         state = normal;
  923.                 }
  924.                 [thetoken   free];
  925.             }
  926.             else
  927.             {
  928.                 theName = [thetoken   GetName];
  929.                 //
  930.                 //    Switch indented strangely so more room on lines.  =(
  931.                 //
  932.                 switch([thetoken   GetType])
  933.                 {
  934.                 case tokenBeginGroup:
  935.                     [destinationFile   WriteToken: thetoken];
  936.                     GroupDepth ++;
  937.                     [self StartNewGroup];    // 93.04.03  added for char convert bugfix
  938.                     break;
  939.                 case tokenEndGroup:
  940.                     GroupDepth --;
  941.                     [destinationFile   WriteToken: thetoken];
  942.                     [self FinishGroup];    // 93.04.03  added for char convert bugfix
  943.                     break;
  944.                 case tokenControlWord:
  945.                     //
  946.                     //    Process a control word, watching for special cases:
  947.                     //        \f
  948.                     //        \colortbl
  949.                     //        \fonttbl
  950.                     //        \pict
  951.                     //        \ul
  952.                     //        \bullet
  953.                     //        \emdash
  954.                     //        \endash
  955.                     //        \lquote
  956.                     //        \rquote
  957.                     //        \ldblquote
  958.                     //        \rdblquote
  959.                     //
  960.                     if (strcmp(theName, "f") == 0)
  961.                     {
  962.                         //
  963.                         //    Determine whether text in this typeface should be
  964.                         //    converted or not.  store the value on the stack.
  965.                         //
  966.                         myEntry = [fontTable valueForKey:
  967.                             (const void *) [thetoken   GetValue]];
  968.                         if (myEntry == nil)
  969.                             [self SetCurrentConverstionTo: FullConversion];
  970.                         else
  971.                         {
  972.                             if ([myEntry   GetConversion] == FullConversion)
  973.                                 [self SetCurrentConverstionTo: FullConversion];
  974.                             else
  975.                                 [self SetCurrentConverstionTo: NoConversion];
  976.                         }
  977.                         [destinationFile   WriteToken: thetoken];
  978.                     }
  979.                     else if (strncmp(theName, "colortbl", 8) == 0)
  980.                     {
  981.                         state = inColorTable;
  982.                         colorStartDepth = GroupDepth -1;
  983.                         if (colorsUsed == YES)
  984.                             [destinationFile   WriteToken: thetoken];
  985.                         else
  986.                         {
  987.                             [destinationFile   ClearThroughLastBegin];
  988.                             [thetoken   free];
  989.                         }
  990.                     }
  991.                     else    if (strcmp(theName, "fonttbl") == 0)
  992.                     {
  993.                         state = inFontTable;
  994.                         fontStartDepth = GroupDepth -1;
  995.                         [destinationFile   WriteToken: thetoken];
  996.                     }
  997.                     else if (strcmp(theName, "pict") == 0)
  998.                     {
  999.                         //
  1000.                         //    If we've found a Mac image, then call the convert routine
  1001.                         //
  1002.                         [destinationFile   WriteToken: thetoken];
  1003.                         thetoken = [sourceFile   GetToken];
  1004.                         FreeCString(theName);
  1005.                         theName = [thetoken  GetName];
  1006.                         [destinationFile   WriteToken: thetoken];
  1007.                         if (strcmp(theName, "macpict") == 0)
  1008.                             [self   ConvertMacPictImageIn: sourceFile  To: destinationFile];
  1009.                         [myManager   SetPercentageDone:
  1010.                             (modifier * [sourceFile  GetCurrentPosition]) + 30];
  1011.                     }
  1012.                     else if ((LookingForUL == YES) && (ConvertSource == NeXTRTF) &&
  1013.                             (strcmp(theName, "ul") == 0) && ([thetoken  GetValue] == 0) )
  1014.                     {
  1015.                         //
  1016.                         //    We we're still looking for \ul0, and we found it, zap it.
  1017.                         //
  1018.                         [thetoken    free];
  1019.                         LookingForUL = NO;
  1020.                     }
  1021.                     //
  1022.                     //    93.07.07    djb    Added bullet, lquote, rquote, ldblquote, and rdblquote
  1023.                     //                conversions.  Note that they are only done when converting
  1024.                     //                a Mac file, since NeXT stuff should not be generating these.
  1025.                     //                As always, these reveal some problems with the kinda kludgey
  1026.                     //                parser I have: Minorly, maybe I should be converting them
  1027.                     //                when going out.  More significantly, this writing out of single
  1028.                     //                characters as word tokens runsthe risk of these being broken up
  1029.                     //                from their associated text, which we may not want.  Neither
  1030.                     //                issue is very large, but.... it bugs the perfectionist in me.
  1031.                     //
  1032.                     else if (ConvertSource == MacRTF)
  1033.                     {
  1034.                         if (strcmp(theName, "bullet") == 0)
  1035.                         {
  1036.                             temptoken=[[rtfToken alloc] initTokenOfType: tokenWord];
  1037.                             [temptoken   SetTokenName:   "•"];
  1038.                             [destinationFile   WriteToken: temptoken];
  1039.                         }
  1040.                         else if (strcmp(theName, "emdash") == 0)
  1041.                         {
  1042.                             temptoken=[[rtfToken alloc] initTokenOfType: tokenWord];
  1043.                             [temptoken   SetTokenName:   "—"];
  1044.                             [destinationFile   WriteToken: temptoken];
  1045.                         }
  1046.                         else if (strcmp(theName, "endash") == 0)  // 93.10.15 djb fixed from emdash
  1047.                         {
  1048.                             temptoken=[[rtfToken alloc] initTokenOfType: tokenWord];
  1049.                             [temptoken   SetTokenName:   "–"];
  1050.                             [destinationFile   WriteToken: temptoken];
  1051.                         }
  1052.                         else if (strcmp(theName, "lquote") == 0)
  1053.                         {
  1054.                             temptoken=[[rtfToken alloc] initTokenOfType: tokenWord];
  1055.                             [temptoken   SetTokenName:   "`"];
  1056.                             [destinationFile   WriteToken: temptoken];
  1057.                         }
  1058.                         else if (strcmp(theName, "rquote") == 0)
  1059.                         {
  1060.                             temptoken=[[rtfToken alloc] initTokenOfType: tokenWord];
  1061.                             [temptoken   SetTokenName:   "'"];
  1062.                             [destinationFile   WriteToken: temptoken];
  1063.                         }
  1064.                         else if (strcmp(theName, "ldblquote") == 0)
  1065.                         {
  1066.                             temptoken=[[rtfToken alloc] initTokenOfType: tokenWord];
  1067.                             [temptoken   SetTokenName:   "“"];
  1068.                             [destinationFile   WriteToken: temptoken];
  1069.                         }
  1070.                         else if (strcmp(theName, "rdblquote") == 0)
  1071.                         {
  1072.                             temptoken=[[rtfToken alloc] initTokenOfType: tokenWord];
  1073.                             [temptoken   SetTokenName:   "”"];
  1074.                             [destinationFile   WriteToken: temptoken];
  1075.                         }
  1076.                         else    // Added this so that we don't loose the token if it is none of the above.
  1077.                             [destinationFile   WriteToken: thetoken];
  1078.                     }
  1079.                     else
  1080.                         [destinationFile   WriteToken: thetoken];
  1081.                     break;
  1082.                 case tokenControlSymbol:
  1083.                     //
  1084.                     //    If the token isn't a \',  or a \<newline>, write it out. 
  1085.                     //    Otherwise, process it some.
  1086.                     //
  1087.                     if ((strcmp(theName, "\n")== 0) || (strcmp(theName, "\r") == 0))
  1088.                     {
  1089.                         //
  1090.                         //    Token is escaped end of line character.  Convert to a \par.
  1091.                         //    Note: Generally \EOL seems to be treated the same
  1092.                         //    as \par.  But \EOL isn't explicitly defined in the spec I have,
  1093.                         //    while \par is.  Thus, my preference for it.
  1094.                         //
  1095.                         [thetoken   free];
  1096.                         thetoken=[[rtfToken alloc] initTokenOfType: tokenControlWord];
  1097.                         [thetoken   SetTokenName:   "par"];
  1098.                         [destinationFile   WriteToken: thetoken];
  1099.                     }
  1100.                     else if (strcmp(theName, "\'") == 0)
  1101.                     {
  1102.                         //
  1103.                         //    If the current type font is to be converted, convert the chars.
  1104.                         //
  1105.                         if ([self  GetCurrentConversion] == NoConversion)
  1106.                         {
  1107.                             if (ConvertSource == NeXTRTF)
  1108.                                 [destinationFile   WriteToken: thetoken];
  1109.                             else
  1110.                             {
  1111.                                 tempToken = [[rtfToken alloc]
  1112.                                         initTokenOfType: tokenWord];
  1113.                                 tempString = NewCString(1);
  1114.                                 tempString[0] = (Character) [thetoken   GetValue];
  1115.                                 tempString[1] = EndOfCString;
  1116.                                 [tempToken   SetTokenName:   tempString];
  1117.                                 [destinationFile   WriteToken: tempToken];
  1118.                                 FreeCString(tempString);
  1119.                                 [thetoken   free];
  1120.                             }
  1121.                         }
  1122.                         else
  1123.                         {
  1124.                             [self   ConvertThisChar: (Character)  [thetoken   GetValue]
  1125.                                 Into: destinationFile];
  1126.                             [thetoken  free];
  1127.                         }
  1128.                     }
  1129.                     else
  1130.                         [destinationFile   WriteToken: thetoken];
  1131.                     break;
  1132.                 case tokenWord:
  1133.                     //
  1134.                     //    If we've found a word, stop looking for \ul0
  1135.                     //    Then, convert the word we found.
  1136.                     //
  1137.                     LookingForUL = NO;
  1138.                     [self   ConvertWordIn: thetoken To: destinationFile
  1139.                         WithConversion: [self  GetCurrentConversion] ];
  1140.                     [thetoken free];
  1141.                     break;
  1142.                 case tokenNoToken:
  1143.                     //
  1144.                     //    This is here mainly to make the compiler stop telling me that
  1145.                     //    tokenNoToken isn't in the switch.  Free the token while we're at it.
  1146.                     //
  1147.                     [thetoken   free];
  1148.                     break;
  1149.                 }
  1150.                 FreeCString(theName);
  1151.             }
  1152.             //
  1153.             //    Tell our manager how we're doing, and get a new token.
  1154.             //
  1155.             tokenCount++;
  1156.             if ((tokenCount %100) == 0)
  1157.                 [myManager   SetPercentageDone:
  1158.                     (modifier * [sourceFile  GetCurrentPosition]) + 30];
  1159.             thetoken = [sourceFile   GetToken];
  1160.         }
  1161.         [thetoken   free];    // Free the token that was last retrieved
  1162.     }
  1163.     tableState = [fontTable initState]; 
  1164.     while  ([fontTable   nextState: &tableState
  1165.             key: (void **)&fontNumber
  1166.             value: (const void **)&myEntry] == YES) 
  1167.         {[myEntry  free];}
  1168.  
  1169.     [fontTable   free];
  1170.     return self;
  1171. }
  1172.  
  1173.  
  1174. //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  1175. //    Routine:        ConvertWordIn:To:WithConversion:
  1176. //    Parameters:    The word token we should be writing out, and the file to write it into. 
  1177. //    Returns:        self
  1178. //    Stores:        none
  1179. //    Description:
  1180. //        This does the generally simple (?) task of writing a word token out.
  1181. //        If w are converting the current font, then we go through the string a character
  1182. //        at a time, and convert the letters.  If we are altering the quotes, we also watch for
  1183. //        quote chars and map to the others as appropriate.
  1184. //    Bugs
  1185. //        Shouldn't we watching for non-printable ascii chars if we aren't
  1186. //        converting anyway?
  1187. //    History:
  1188. //        93.01.02    djb    Broke off from ConvertFrom:To:, and commented!
  1189. //        93.04.03    djb    Modified so ConvertChars is of ConvertTypes
  1190. //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  1191. - ConvertWordIn: thetoken  To: destinationFile WithConversion: (ConvertTypes) ConvertChars
  1192. {
  1193.     CString            nameStart;
  1194.     CString            namePtr        = [thetoken   GetName];
  1195.     Character        aCharacter;
  1196.     PositiveInteger    index        = 0;
  1197.     Instance            myToken;
  1198.     Instance            quoteToken;
  1199.     Character        theQuote;
  1200.     CString            subPart;
  1201.     PositiveInteger    subloc;
  1202.     
  1203.     nameStart = namePtr;
  1204.     //
  1205.     //    Write the word out, modifying any characters we need to.
  1206.     //
  1207.     if (AlterSingleQuotes == NO)
  1208.     {
  1209.         //
  1210.         //    If we are not converting the current font, just write the word.
  1211.         //    Otherwise, step through each character.  If you find a character that is
  1212.         //    not in the printable range, write out everything up to it, and then
  1213.         //    have the char converted and printed out, and then continue.
  1214.         //
  1215.         if (ConvertChars == NoConversion)
  1216.         {
  1217.             myToken = [[rtfToken alloc] initTokenOfType: tokenWord];
  1218.             [myToken   SetTokenName: namePtr];
  1219.             [destinationFile   WriteToken: myToken];
  1220.         }
  1221.         else
  1222.         {
  1223.             while (namePtr[index] != EndOfCString)
  1224.             {
  1225.                 aCharacter = namePtr[index];
  1226.                 if ((aCharacter < 32) || (aCharacter > 126))
  1227.                 {
  1228.                     namePtr[index] = EndOfCString;
  1229.                     myToken = [[rtfToken alloc] initTokenOfType: tokenWord];
  1230.                     [myToken   SetTokenName: namePtr];
  1231.                     [destinationFile   WriteToken: myToken];
  1232.                     namePtr = &namePtr[++index];
  1233.                     index = 0;
  1234.                     [self   ConvertThisChar: aCharacter  Into: destinationFile];
  1235.                 }
  1236.                 else
  1237.                     index++;
  1238.             }
  1239.             //
  1240.             //    If there is still a chunk waiting to be written.
  1241.             //
  1242.             if (index != 0)
  1243.             {
  1244.                 myToken = [[rtfToken alloc] initTokenOfType: tokenWord];
  1245.                 [myToken   SetTokenName: namePtr];
  1246.                 [destinationFile   WriteToken: myToken];
  1247.             }
  1248.         }
  1249.     }
  1250.     else
  1251.     {
  1252.         //
  1253.         //    Since we are converting quotes, we must examine every character in
  1254.         //    the string.
  1255.         //
  1256.         while (namePtr[index] != EndOfCString)
  1257.         {
  1258.             //
  1259.             //    Look for either start or end quote in namePtr.  Store the character
  1260.             //    in theQuote (If it failed to find a quote, it returns the index to the null
  1261.             //    terminator)
  1262.             //
  1263.             index = strcspn(namePtr, "`\'");
  1264.             theQuote = namePtr[index];
  1265.             //
  1266.             //    Don't do this work if the quote was the first character found.
  1267.             //
  1268.             if (index != 0)
  1269.             {
  1270.                 //
  1271.                 //    We don't need the quote character any longer, so make it a
  1272.                 //    null terminator so we can easily process the portion of the string
  1273.                 //    that came before the quote.
  1274.                 //
  1275.                 namePtr[index] = EndOfCString;
  1276.                 if (ConvertChars == NoConversion)
  1277.                 {
  1278.                     myToken = [[rtfToken alloc] initTokenOfType: tokenWord];
  1279.                     [myToken   SetTokenName: namePtr];
  1280.                     [destinationFile   WriteToken: myToken];
  1281.                 }
  1282.                 else
  1283.                 {
  1284.                     //
  1285.                     //    We are converting the string, so walk through the part up to
  1286.                     //    the quote, and convert any special characters we find.
  1287.                     //
  1288.                     subPart = namePtr;
  1289.                     subloc = 0;
  1290.                     while (subPart[subloc] != EndOfCString)
  1291.                     {
  1292.                         aCharacter = subPart[subloc];
  1293.                         if ((aCharacter < 32) || (aCharacter > 126))
  1294.                         {
  1295.                             //
  1296.                             //    we found a character that needs converting.  Soooo.
  1297.                             //    make IT a null, write out that which went before, and
  1298.                             //    then convert it and continue.
  1299.                             //
  1300.                             subPart[subloc] = EndOfCString;
  1301.                             myToken = [[rtfToken alloc] initTokenOfType: tokenWord];
  1302.                             [myToken   SetTokenName: subPart];
  1303.                             [destinationFile   WriteToken: myToken];
  1304.                             [self   ConvertThisChar: aCharacter  Into: destinationFile];
  1305.                             subPart = &subPart[++subloc];
  1306.                             subloc = 0;
  1307.                         }
  1308.                         else
  1309.                             subloc++;
  1310.                     }
  1311.                     if (subloc != 0)
  1312.                     {
  1313.                         //
  1314.                         //    If there are still characters waiting to be written out, do so.
  1315.                         //
  1316.                         myToken = [[rtfToken alloc] initTokenOfType: tokenWord];
  1317.                         [myToken   SetTokenName: subPart];
  1318.                         [destinationFile   WriteToken: myToken];
  1319.                     }
  1320.                 }
  1321.             }
  1322.             //
  1323.             //    Having written out the text that comes before the quote, now
  1324.             //    write out the quote itself.
  1325.             //
  1326.             switch (theQuote)
  1327.             {
  1328.                 case EndOfCString :
  1329.                     index--;
  1330.                     break;
  1331.                 case '\'' :
  1332.                     quoteToken = [[rtfToken alloc] initTokenOfType: tokenControlSymbol];
  1333.                     [quoteToken SetTokenName: "\'"];
  1334.                     if (ConvertSource == MacRTF)
  1335.                         [quoteToken   SetTokenValue: (Character) '’'];
  1336.                     else
  1337.                         [quoteToken   SetTokenValue: (Character) 0xD5];  //mac curley quote
  1338.                     [destinationFile   WriteToken: quoteToken];
  1339.                     break;
  1340.                 case '`' :
  1341.                     quoteToken = [[rtfToken alloc] initTokenOfType: tokenControlSymbol];
  1342.                     [quoteToken SetTokenName: "\'"];
  1343.                     if (ConvertSource == MacRTF)
  1344.                         [quoteToken   SetTokenValue: (Character) 'ˋ'];
  1345.                     else
  1346.                         [quoteToken   SetTokenValue: (Character) 0xD4];  //mac curley quote
  1347.                     [destinationFile   WriteToken: quoteToken];
  1348.                     break;
  1349.             }
  1350.             //
  1351.             //    Set namePtr to point just beyond the quote.
  1352.             //
  1353.             namePtr = &namePtr[index+1];
  1354.             //    93.01.09    djb    Wasn't resetting index.  DUH.  (final pre-beta 1.0 bug)
  1355.             index = 0;
  1356.         }
  1357.     }
  1358.     FreeCString(nameStart);
  1359.     return self;
  1360. }
  1361.  
  1362.  
  1363.  
  1364. //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  1365. //    Routine:        ConvertThisChar:Into: 
  1366. //    Parameters:    A character we are reading from, and a file to write into.
  1367. //    Returns:        self
  1368. //    Stores:        none
  1369. //    Description:
  1370. //        Given a character, this does any conversions needed, and writes it out to
  1371. //        the destination file.
  1372. //        Conversions that might be needed are: Convert between Mac and NeXT
  1373. //        character sets.  Change some ctrl characters to rtf things (e.g. tab char to \tab).
  1374. //        Conversion to a \' notation if appropriate and going to the Mac.
  1375. //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  1376. - ConvertThisChar: (Character) aCharacter Into: destinationFile
  1377. {
  1378.     Character    newCharacter;
  1379.     Boolean        usedSymbol;
  1380.     Instance        thetoken;
  1381.     CString        tempString;
  1382.     CString        newString;
  1383.     
  1384.     if (aCharacter == '\t')
  1385.     {
  1386.         thetoken=[[rtfToken alloc] initTokenOfType: tokenControlWord];
  1387.         [thetoken   SetTokenName:   "tab"];
  1388.         [destinationFile   WriteToken: thetoken];
  1389.     }
  1390.     else
  1391.     {
  1392.         //
  1393.         //    Try to map the character to the other character set.
  1394.         //        If we sould not map the char to a single dest character (e.g. NeXt's
  1395.         //            thorn character must be converted to [thorn])
  1396.         //        If we could, then check if the result required Symbol.  If so, then
  1397.         //            write a proper font change out.
  1398.         //        Otherwise, just write out the charaction.
  1399.         //
  1400.         newCharacter = [CharConverter   ConvertCharacter: aCharacter];
  1401.         if ([CharConverter   GetErrorCode] == errCANTMAPTOONE)
  1402.         {
  1403.             //
  1404.             //    If we could not map our character to a single destination character,
  1405.             //    Then, resort to this hack.  Store the character in a tiny string.  Ask the
  1406.             //    converter to convert it with it's string converter, which will give us a longer
  1407.             //    version of the character (e.g. theNeXT thorn character becomes [thorn]),
  1408.             //    turn this into a token, and write it out.  
  1409.             //
  1410.             tempString = NewCString(1);
  1411.             tempString[0] = newCharacter;
  1412.             tempString[1] = EndOfCString;
  1413.             newString = [CharConverter   ConvertString: tempString WithLength: 1];
  1414.             thetoken = [[rtfToken alloc] initTokenOfType: tokenWord];
  1415.             [thetoken   SetTokenName:   newString];
  1416.             [destinationFile   WriteToken: thetoken];
  1417.             FreeCString(tempString);
  1418.             FreeCString(newString);
  1419.         }
  1420.         else
  1421.         {
  1422.             usedSymbol = [CharConverter   GetBooleanFrom: SECOND_RESULT];
  1423.             //
  1424.             //    If we converted a mac source character, and it will only map to a symbol
  1425.             //    character, then write out the introduction to the symbol sequence: 
  1426.             //    {\f23  (or whatever number symbol is)
  1427.             //
  1428.             if  (usedSymbol == YES)
  1429.             {
  1430.                 thetoken=[[rtfToken alloc] initTokenOfType: tokenBeginGroup];
  1431.                 [destinationFile   WriteToken: thetoken];
  1432.                 thetoken=[[rtfToken alloc] initTokenOfType: tokenControlWord];
  1433.                 [thetoken   SetTokenName:   "f"];
  1434.                 [thetoken   SetTokenValue:   (Integer) SymbolNumber];
  1435.                 [destinationFile   WriteToken: thetoken];
  1436.             }
  1437.             //
  1438.             //    In all cases, build the proper token type for the converted character
  1439.             //    (a new \' or a word token,  depending on whether the character is > or < 126),
  1440.             //    and write it out. (I'm taking the liberty of putting ctrl characters out as
  1441.             //    control symbols as well)  Also, note that we do not convert to \', but always
  1442.             //    to a litteral character when translating TO the NeXT.  This is because
  1443.             //    I've not seen Edit write out these commands, and edit mis-displayed some
  1444.             //    chars (0xDF-0xFF converted to no visible character in Times-Roman,
  1445.             //    when they very well should have) that were in this notation.
  1446.             //
  1447.             if ( ((newCharacter > 126) || (newCharacter < 32))
  1448.                 && (ConvertSource == NeXTRTF))
  1449.             {
  1450.                 thetoken = [[rtfToken alloc] initTokenOfType: tokenControlSymbol];
  1451.                 [thetoken   SetTokenName:   "\'"];
  1452.                 [thetoken   SetTokenValue:   (Integer) newCharacter];
  1453.             }
  1454.             else
  1455.             {
  1456.                 thetoken = [[rtfToken alloc] initTokenOfType: tokenWord];
  1457.                 tempString = NewCString(1);
  1458.                 tempString[0] = newCharacter;
  1459.                 tempString[1] = EndOfCString;
  1460.                 [thetoken   SetTokenName:   tempString];
  1461.                 FreeCString(tempString);
  1462.             }
  1463.             [destinationFile   WriteToken: thetoken];
  1464.             //
  1465.             //    If we are, building the symbol sequence, add the closing group token.
  1466.             //
  1467.             if (usedSymbol == YES)
  1468.             {
  1469.                 thetoken=[[rtfToken alloc] initTokenOfType: tokenEndGroup];
  1470.                 [destinationFile   WriteToken: thetoken];
  1471.             }
  1472.         }
  1473.     }
  1474.     return self;
  1475. }
  1476.  
  1477.  
  1478.  
  1479. //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  1480. //    Routine:        ConvertMacPictImageIn:To: 
  1481. //    Parameters:    The rtf file containing the picture info, and the file to write any stuff to.
  1482. //    Returns:        self
  1483. //    Stores:        none
  1484. //    Description:
  1485. //        Note 'To:', above, referes to the rtffile that is being converted into, not where we should
  1486. //        convert the pict to.
  1487. //
  1488. //        In brief, consuming picture data from the source file, and treat in some way according
  1489. //        to user preferences.
  1490. //        When called, several things are assumed:
  1491. //            - The destination file has had {\pict\macpict written out to it
  1492. //            - The instance variable ExtractPicts will store whether we should write pict
  1493. //                data out as files, or whether we should just discard it
  1494. //            - The instance variable ConvertPicts will have a YES value if we are to try to
  1495. //                get the pict data converted into an eps file
  1496. //            - The variable KeepTokensForMe  will have a YES value if all extra tokens in
  1497. //                the picture group should be kept (this is probably only of interest to me,
  1498. //                hence this  setting is only changable by recompilation)
  1499. //        This will then do several things.  It will discard the items mentiod above in the
  1500. //        destinationFile.   It will then give the rtf file the WordsArePictures hint. 
  1501. //        Following this, tokens are read in one after another.  Ultimately, these are discarded
  1502. //        (unles KeepTokensForMe is YES, in which case they are written to a tiny rtf file).
  1503. //        If we are converting the image, however, we watch each, and figure out what the
  1504. //        width and height of the image should be, and proceed to write proper information
  1505. //        to the destinationFile.  The picture data itself is then encountered.  This is either then
  1506. //        discarded, or stored in a file.  If we have been asked to, we will then pass the
  1507. //        name of this file to the pict converter to be converted.  We then discard the final }.
  1508. //    Bugs
  1509. //        We deal with binary data inadequately.  We should be getting the parameter to
  1510. //        \bin, and reading exactly that number of bytes in.
  1511. //    History:
  1512. //        92.12.24    djb    created initial version
  1513. //        93.01.03    djb    enhanced so it worked completely.
  1514. //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  1515. - ConvertMacPictImageIn: sourceFile To: destinationFile
  1516. {
  1517.     Instance        thetoken;
  1518.     Instance        tokenFile        = NullInstance; // initialized to shut the compiler up
  1519.     Instance        pictFile;
  1520.     TokenType    thetokentype;
  1521.     Integer        groupDepth        = 0;
  1522.     CString        theName;
  1523.     Boolean        BinaryData        = NO;
  1524.     Integer        ImageWidth        = 0;
  1525.     Integer        ImageHeight        = 0;
  1526.     CString        newFilename;        // extensionless name of file inside the .rtfd wrapper
  1527.     CString        baseFilename;
  1528.     CString        charLocation;
  1529.     CString        theDirectory;
  1530.     CString        epsFilename;
  1531.     CString        tempFilename;
  1532.     CString        justFilename;
  1533.     CString        tempName;
  1534.     Boolean        weHaveError            = NO;
  1535.     ByteString        buffer;
  1536.     PositiveInteger    nameLength;
  1537.     PositiveInteger    tempLength;
  1538.     PositiveInteger    index;
  1539.     PositiveInteger    destIndex;
  1540.     port_t            thePort;
  1541.     Instance            ourSpeaker = [NXApp appSpeaker];
  1542.     int                storedTimeout;
  1543.     //
  1544.     //    Increment the count, to show which picture this is (1st, 2nd, etc)
  1545.     //
  1546.     pictNum ++;
  1547.     //
  1548.     //    Compute base name for the file(s) we create here.Get the path to
  1549.     //    the rtfd folder:    /x/y/...z/foo.rtfd
  1550.     //    extract            'foo'                (the tempFilename) 
  1551.     //    build            /x/y/...z/foo.rtfd/foo  (the baseFilename)
  1552.     //    build            foo(picture#0000)    (justFilename)
  1553.     //
  1554.     theDirectory = [destinationFile   GetDirectory];
  1555.     baseFilename = NewCString(strlen(theDirectory)+1);
  1556.     strcpy(baseFilename, theDirectory);
  1557.     strcat(baseFilename, "/");
  1558.  
  1559.     tempFilename = NewCString(strlen(theDirectory));
  1560.     charLocation = strrchr(theDirectory, '/');
  1561.     if (charLocation == NullCString)
  1562.         charLocation = theDirectory; // No /.  Weird.  Use whole dir name.
  1563.     else
  1564.         charLocation++;    // skip over the / that we just found
  1565.     strcpy(tempFilename, charLocation);
  1566.     charLocation = strchr(tempFilename, '.'); // Remove everything after first period
  1567.     if (charLocation != NullCString)
  1568.         charLocation[0] = EndOfCString;    // amputate the string at the period
  1569.  
  1570.     //
  1571.     //    93.02.20    djb    Added this loop to remove any spaces, {'s, }'s, /'s, \'s or .'s
  1572.     //                found in the filename. Removing the \ and }'s is pretty necessary.
  1573.     //                Removing the others is for the sake of sanity.  We do this by walking
  1574.     //                through tempFilename one char at a time.  If the char is an acceptable
  1575.     //                one, we move it to the next spot down in the filename string
  1576.     //                (in the worst case, this will move it onto itself).
  1577.     //
  1578.     tempLength = strlen(tempFilename);
  1579.     destIndex = 0;
  1580.     for (index = 0; index < tempLength; index++)
  1581.     {
  1582.         //
  1583.         //    If the current source character was not one of the ones we are looking for,
  1584.         //    (the default case), we copy this next character to the next available
  1585.         //    destination position in the same string and then increment the destination
  1586.         //    position.
  1587.         //
  1588.         switch (tempFilename[index])
  1589.         {
  1590.             case '\\' :
  1591.             case '}' :
  1592.             case '{' :
  1593.             case '.' :
  1594.             case ' ':
  1595.             case '/' :
  1596.             case '\'' :
  1597.             case '\"' :
  1598.             case '`':
  1599.                 // Do nothing
  1600.                 break;
  1601.             default :                
  1602.                 tempFilename[destIndex] = tempFilename[index];
  1603.                 destIndex++;
  1604.                 break;
  1605.         }
  1606.     }
  1607.     tempFilename[destIndex] = EndOfCString;
  1608.  
  1609.  
  1610.     justFilename = NewCString(strlen(tempFilename)+15);
  1611.     sprintf(justFilename, "%s(picture#%.4ld)", tempFilename, pictNum); // only 999 images...
  1612.     FreeCString(tempFilename);
  1613.     FreeCString(theDirectory);
  1614.     //
  1615.     //    Now, build an extensionless filename of the form
  1616.     //        /x/y/...z/foo.rtfd/foo(picture#0000)
  1617.     //
  1618.     newFilename = NewCString(strlen(baseFilename)+strlen(justFilename));
  1619.     sprintf(newFilename, "%s%s", baseFilename, justFilename); 
  1620.     FreeCString(baseFilename);
  1621.     //
  1622.     //    Create the temporary file.  Note that we totally ignore any error codes. 
  1623.     //
  1624.     if ((KeepTokensForMe == YES) && (ExtractPicts == YES))
  1625.     {
  1626.         tempFilename = NewCString(strlen(newFilename)+7);
  1627.         strcpy(tempFilename, newFilename);
  1628.         strcat(tempFilename, ".tokens");
  1629.         tokenFile = [[rtfFile  alloc] initAndUse: tempFilename];
  1630.         [tokenFile   CreateAndOpenFor: FILE_WRITE];
  1631.         FreeCString(tempFilename);
  1632.     }
  1633.     //
  1634.     //    Discard the pending {\pict\macpict
  1635.     //    And, tell the source file that the next word token it finds may be big
  1636.     //        (and that it can do a hex convert)
  1637.     //
  1638.     (void) [destinationFile   ClearThroughLastBegin];
  1639.     [sourceFile   Hint: WordsArePictures];
  1640.     //
  1641.     //    Read in any and all control tokens before the bitmap.  For some of them, we'll
  1642.     //    want to record particular information for later use.  Most we just ignore.
  1643.     //    (if we have the KeepTokensForMe set to YS, we also store these in our temp file)
  1644.     //
  1645.     thetoken = [sourceFile   GetToken];
  1646.     thetokentype = [thetoken   GetType];
  1647.     while (thetokentype != tokenWord)
  1648.     {
  1649.         switch (thetokentype)
  1650.         {
  1651.             case tokenBeginGroup:
  1652.                 groupDepth++;
  1653.                 break;
  1654.             case tokenEndGroup:
  1655.                 groupDepth--;
  1656.                 break;
  1657.             case tokenControlWord:
  1658.                 theName = [thetoken   GetName];
  1659.                 if (strcmp(theName, "picw") == 0)
  1660.                     ImageWidth = [thetoken  GetValue];
  1661.                 else if (strcmp(theName, "pich") == 0)
  1662.                     ImageHeight = [thetoken  GetValue];
  1663.                 else if (strcmp(theName, "bin") == 0)
  1664.                     BinaryData = YES;
  1665.                 FreeCString(theName);
  1666.                 break;    
  1667.             case tokenControlSymbol:
  1668.             case tokenNoToken:
  1669.             case tokenWord:
  1670.                 //
  1671.                 //    Should be setting some kinda error
  1672.                 //
  1673.                 break;
  1674.         }
  1675.         if ((KeepTokensForMe == YES) && (ExtractPicts == YES))
  1676.             [tokenFile  WriteToken: thetoken];
  1677.         else
  1678.             [thetoken   free];
  1679.         thetoken = [sourceFile   GetToken];
  1680.         thetokentype = [thetoken   GetType];
  1681.     }
  1682.     //
  1683.     //    Close the notes file. 
  1684.     //
  1685.     if ((KeepTokensForMe == YES) && (ExtractPicts == YES))
  1686.         [tokenFile CloseAndSave];
  1687.     //
  1688.     //    Now, write out the proper info into the destination file, if we are converting.
  1689.     //
  1690.     if (ConvertPictures == YES)
  1691.     {
  1692.         //
  1693.         //    93.01.31    djb    We used to be writing the NeXTGraphic out as a series of
  1694.         //                rtf tokens.  This was a bit precarious, and when it broke again
  1695.         //                this time because the line had necessarily grown too long for the
  1696.         //                rtf file, I dediced the rtffile needed a specialized method for writing
  1697.         //                this construct out.  So, all the convoluted stuff that was here has
  1698.         //                been replaced by this one method call.
  1699.         //
  1700.         tempName = NewCString(strlen(justFilename)+4);
  1701.         sprintf(tempName, "%s.eps", justFilename);
  1702.         [destinationFile   WriteNeXTGraphicAt: 0
  1703.             WithName: tempName
  1704.             Width: ImageWidth
  1705.             Height: ImageHeight];
  1706.         FreeCString(tempName);
  1707.     }
  1708.     //
  1709.     //    We have just found a word token, (just before the if statement immediately above,
  1710.     //    that is).  This should be the picture token.  If it isn't, then we're probably
  1711.     //    in deep trouble.  Sigh.  Why doesn the spec say so little about what MUST be
  1712.     //    where?  If we are extracting or converting pictures, we must store this ascii
  1713.     //    data as a binary pict file.
  1714.     //
  1715.     if  ((ConvertPictures == YES) || (ExtractPicts == YES))
  1716.     {
  1717.         //
  1718.         //    If we are not extracting the pictures (storing them as part of the
  1719.         //    .rtfd wrapper), then we will store them as temporary files to be cleaned
  1720.         //    up by the OS later.  Otherwise, we create the .pict file inside the wrapper.
  1721.         //
  1722.         if (ExtractPicts == NO)
  1723.             pictFile = [[File  alloc] initAndUseTemporary];
  1724.         else
  1725.         {
  1726.             tempFilename = NewCString(strlen(newFilename)+5);
  1727.             strcpy(tempFilename, newFilename);
  1728.             strcat(tempFilename, ".pict");
  1729.             pictFile = [[File  alloc] initAndUse: tempFilename];
  1730.             FreeCString(tempFilename);
  1731.         }
  1732.         //
  1733.         //    Open the file, and write a vanity pict header.
  1734.         //
  1735.         [pictFile   CreateAndOpenFor: FILE_WRITE];
  1736.         buffer = NewByteString(512);
  1737.         for (index = 0; index < 512; index+=16)
  1738.             strcpy((CString)&buffer[index], "Convert_RTF V1.1");
  1739.         [pictFile   Write: 512 BytesFrom: buffer];
  1740.         FreeByteString(buffer);
  1741.         //
  1742.         //    Now, write out the picture data.
  1743.         //        If the picture data is binary, then we've got trouble because we read it
  1744.         //        in wrong because we ignored the argument to \bin above.  
  1745.         //
  1746.         if (BinaryData == YES)
  1747.         {
  1748.             // uh oh....
  1749.         }
  1750.         else
  1751.         {
  1752.             //
  1753.             //    Get a copy of the hex version of the picture data.  
  1754.             //
  1755.             theName = [thetoken   GetName];
  1756.             nameLength = strlen(theName);
  1757.             buffer = [self  Convert: nameLength BytesToBinFrom: (ByteString) theName];
  1758.             [pictFile   Write: nameLength / 2 BytesFrom: buffer];
  1759.             FreeByteString(buffer);
  1760.             FreeCString(theName);
  1761.         }
  1762.         [pictFile   CloseAndSave];
  1763.         //
  1764.         //    Finally, invoke the convert pict app, and ask it to convert the stuff.
  1765.         //
  1766.         if (ConvertPictures == YES)
  1767.         {
  1768.             thePort = NXPortFromName("Convert_PICT", NULL);
  1769.             if (thePort != PORT_NULL)
  1770.             {
  1771.                 PictConverterIsOpen = YES;
  1772.                 //
  1773.                 //    Set up the filenames, and call the IAC method....
  1774.                 //
  1775.                 epsFilename = NewCString(strlen(newFilename)+5);
  1776.                 strcpy(epsFilename, newFilename);
  1777.                 strcat(epsFilename, ".eps");
  1778.                 tempFilename = [pictFile   GetPathname];
  1779.                 ourSpeaker = [NXApp appSpeaker];
  1780.                 storedTimeout = [ourSpeaker  sendTimeout];
  1781.                 [ourSpeaker   setSendTimeout: 120000]; //60,000ms.  2 mintues.
  1782.                 [ourSpeaker   setSendPort: thePort ];
  1783.                 [ourSpeaker   msgConvert: tempFilename To: epsFilename];
  1784.                 [ourSpeaker   setSendTimeout: storedTimeout]; //restore it.
  1785.                 FreeCString(tempFilename);
  1786.                 FreeCString(epsFilename);
  1787.             }
  1788.         }
  1789.     }
  1790.     //
  1791.     //    Discard the picture data.
  1792.     //
  1793.     [thetoken   free];
  1794.     //
  1795.     //    Finally, read in the last token, which had better be the } token.
  1796.     //
  1797.     thetoken = [sourceFile   GetToken];
  1798.     if ( ([thetoken   GetType] != tokenEndGroup)  || (groupDepth != 0) )
  1799.         weHaveError = YES;
  1800.     [thetoken   free];
  1801.     //
  1802.     //    Undo the setting stored above, and free strings.
  1803.     //
  1804.     [destinationFile   Hint: WordsAreNotPictures];
  1805.     FreeCString(newFilename);
  1806.     FreeCString(justFilename);
  1807.     return self;
  1808. }
  1809.  
  1810.  
  1811.  
  1812. //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  1813. //    Routine:        Convert:BytesToBinFrom: 
  1814. //    Parameters:    A source buffer and it's length
  1815. //    Returns:        A pointer to a buffer of the converted data
  1816. //    Stores:        none
  1817. //    Description:
  1818. //        This takes a buffer of the form "0A0053FD..." (that is, a hex representation of
  1819. //        binary data), and returns a buffer with 0x0A0053FD...  in it (binary rep).
  1820. //        It does this with an algorithm that probably is not optimal, that converts the
  1821. //        individual nibbles (each represented by a char of ascii) into a binary form.
  1822. //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  1823. - (ByteString) Convert: (Integer) sourceSize BytesToBinFrom: (ByteString) source
  1824. {
  1825.     Integer            nibblecnt;
  1826.     Byte            result;
  1827.     Character        thenibble;
  1828.     ByteString        buffer;
  1829.     PositiveInteger    bufferSize;
  1830.     PositiveInteger    bufferIndex    = 0;
  1831.     PositiveInteger    index;
  1832.  
  1833.     bufferSize = sourceSize / 2;    // The source had BETTER be an even bytes length. 
  1834.     buffer = NewByteString(bufferSize);
  1835.     for (index = 0; index < sourceSize; index+=2)
  1836.     {
  1837.         thenibble = source[index];
  1838.         result = 0;
  1839.         //
  1840.         //    GROSS algorithm.  Loop through switch twice.  First time using nibble1,
  1841.         //    second time using nibble2.  This allows us to use this switch for each nibble.
  1842.         //
  1843.         for (nibblecnt = 0; nibblecnt < 2; nibblecnt++)
  1844.         {
  1845.             result = result << 4;   // 1st, shift nothing, 2nd time shift 1st nible to high nibble. 
  1846.             switch (thenibble)
  1847.             {
  1848.                 case '0':
  1849.                 case '1':
  1850.                 case '2':
  1851.                 case '3':
  1852.                 case '4':
  1853.                 case '5':
  1854.                 case '6':
  1855.                 case '7':
  1856.                 case '8':
  1857.                 case '9':
  1858.                     // subtracting '0' (0x30) from a character gives binary #
  1859.                     result += (Byte) (thenibble - '0');                  break;
  1860.                 case 'A':
  1861.                 case 'B':
  1862.                 case 'C':
  1863.                 case 'D':
  1864.                 case 'E':
  1865.                 case 'F':
  1866.                     result += (Byte)  ((thenibble - 'A')  + 10); 
  1867.                     break;
  1868.                 case 'a':
  1869.                 case 'b':
  1870.                 case 'c':
  1871.                 case 'd':
  1872.                 case 'e':
  1873.                 case 'f':
  1874.                     result += (Byte)  ((thenibble - 'a')  + 10); 
  1875.                     break;
  1876.                 default:
  1877.                     result += 0;    // This is really an error condition.
  1878.                     break;
  1879.             }
  1880.             thenibble =  source[index+1];
  1881.         }
  1882.         buffer[bufferIndex] = result;
  1883.         bufferIndex++;
  1884.     }
  1885.     return buffer;
  1886. }
  1887.  
  1888.  
  1889. //
  1890. //    93.04.04    djb    These methods were added today to fix a final bug in the
  1891. //    converter.  Ideally, they should actually be implemented by another object.
  1892. //    However, it's not that important, and it will be easier to put them here
  1893. //    I just want to get this done.  If I ever fix this converter so it works as
  1894. //    it should (a real parser, for instance) then these should move into their
  1895. //    own 'stack object'.  Anyway.  These routines will manipulate a stack of
  1896. //    flags indicating whether text should be converted or not.  See above for
  1897. //    more details.
  1898. //
  1899.  
  1900.  
  1901. //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  1902. //    Routine:        ResetStack
  1903. //    Parameters:    none
  1904. //    Returns:        self
  1905. //    Stores:        none
  1906. //    Description:
  1907. //        This will set the current stack location back to 0.  It also sets the first value on
  1908. //        the stack to use the current default conversion value the user has specified using
  1909. //        the preferences. It could legitimately free
  1910. //        the current stack and remalloc a small one, in the case that the last time the
  1911. //        stack got much too large.  But, this is unlikely, so this won't.
  1912. //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  1913. - ResetStack
  1914. {
  1915.     StackLocation = 0;
  1916.  
  1917.     if (ConvertAllText == ConvertAll)
  1918.         CharConvertStack[StackLocation] = FullConversion;
  1919.     else
  1920.         CharConvertStack[StackLocation] = NoConversion;
  1921.  
  1922.     return self;
  1923. }
  1924.  
  1925. //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  1926. //    Routine:        StartNewGroup
  1927. //    Parameters:    none
  1928. //    Returns:        self
  1929. //    Stores:        none
  1930. //    Description:
  1931. //        This causes a new conversion value to be pushed on the stack.  The value is
  1932. //        duplicated from what was previously on the top of the stack.  Thus, if ConvertAll
  1933. //        was on the top of the stack, this pushes a new value of ConvertAll on the stack.
  1934. //        This is because a new group always will start out with the same font that was
  1935. //        used in the surrounding group.  This value can be changed later.
  1936. //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  1937. - StartNewGroup
  1938. {
  1939.     ConvertTypes    *tempStack;
  1940.  
  1941.     StackLocation++;
  1942.     if (StackLocation > StackTop)
  1943.     {
  1944.         tempStack = malloc((1+StackTop)*sizeof(ConvertTypes) * 2);
  1945.         memcpy(tempStack, CharConvertStack, (1+StackTop)*sizeof(ConvertTypes));
  1946.         StackTop  = ((StackTop+1)*2) -1;
  1947.         free(CharConvertStack);
  1948.         CharConvertStack = tempStack;
  1949.     }
  1950.     CharConvertStack[StackLocation] = CharConvertStack[StackLocation-1];
  1951.  
  1952.     return self;
  1953. }
  1954.  
  1955. //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  1956. //    Routine:        FinishGroup
  1957. //    Parameters:    none
  1958. //    Returns:        self
  1959. //    Stores:        none
  1960. //    Description:
  1961. //        When we finish a group, we simply pop the top element off the stack so we revert
  1962. //        back to the conversion value for the surrounding group
  1963. //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  1964. - FinishGroup
  1965. {
  1966.     StackLocation--;
  1967.     if (StackLocation < 0)
  1968.         StackLocation = 0;
  1969.  
  1970.     return self;
  1971. }
  1972.  
  1973. //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  1974. //    Routine:        ChangeCurrentConverstionTo:
  1975. //    Parameters:    The value to be replace the value at the top of the stack.
  1976. //    Returns:        self
  1977. //    Stores:        none
  1978. //    Description:
  1979. //        In violation of a pristine stack paradigm, this will replace the value on the top
  1980. //        of the stack with the specified value.  This allows the top value to reflect whatever
  1981. //        the conversion is for the current group properly.  This could actually be modeled
  1982. //        as a pop and a push.
  1983. //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  1984. - SetCurrentConverstionTo: (ConvertTypes) newValue
  1985. {
  1986.     CharConvertStack[StackLocation] = newValue;
  1987.     return self;
  1988. }
  1989.  
  1990. //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  1991. //    Routine:        GetCurrentConverstion:
  1992. //    Parameters:    none
  1993. //    Returns:        the ConvertTypes that is on the top of the stack
  1994. //    Stores:        none
  1995. //    Description:
  1996. //        In violation of a pristine stack paradigm, this will replace the value on the top
  1997. //        of the stack with the specified value.  This allows the top value to reflect whatever
  1998. //        the conversion is for the current group properly.  This could actually be modeled
  1999. //        as a pop and a push.
  2000. //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  2001. - (ConvertTypes) GetCurrentConversion
  2002. {
  2003.     return CharConvertStack[StackLocation];
  2004. }
  2005.  
  2006.  
  2007. @end
  2008.  
  2009.